home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / common / tga.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-02-02  |  29.8 KB  |  1,244 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * $Id: tga.c,v 1.23.2.3 2002/01/24 07:42:10 nicklamb Exp $
  5.  * TrueVision Targa loading and saving file filter for the Gimp.
  6.  * Targa code Copyright (C) 1997 Raphael FRANCOIS and Gordon Matzigkeit
  7.  *
  8.  * The Targa reading and writing code was written from scratch by
  9.  * Raphael FRANCOIS <fraph@ibm.net> and Gordon Matzigkeit
  10.  * <gord@gnu.ai.mit.edu> based on the TrueVision TGA File Format
  11.  * Specification, Version 2.0:
  12.  *
  13.  *   <URL:ftp://ftp.truevision.com/pub/TGA.File.Format.Spec/>
  14.  *
  15.  * It does not contain any code written for other TGA file loaders.
  16.  * Not even the RLE handling. ;)
  17.  *
  18.  * This program is free software; you can redistribute it and/or modify
  19.  * it under the terms of the GNU General Public License as published by
  20.  * the Free Software Foundation; either version 2 of the License, or
  21.  * (at your option) any later version.
  22.  *
  23.  * This program is distributed in the hope that it will be useful,
  24.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  25.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  26.  * GNU General Public License for more details.
  27.  *
  28.  * You should have received a copy of the GNU General Public License
  29.  * along with this program; if not, write to the Free Software
  30.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  31.  */
  32.  
  33. /*
  34.  * Modified August-November 2000, Nick Lamb <njl195@zepler.org.uk>
  35.  *   - Clean-up more code, avoid structure implementation dependency,
  36.  *   - Load more types of images reliably, reject others firmly
  37.  *   - This is not perfect, but I think it's much better. Test please!
  38.  *
  39.  * Release 1.2, 1997-09-24, Gordon Matzigkeit <gord@gnu.ai.mit.edu>:
  40.  *   - Bug fixes and source cleanups.
  41.  *
  42.  * Release 1.1, 1997-09-19, Gordon Matzigkeit <gord@gnu.ai.mit.edu>:
  43.  *   - Preserve alpha channels.  For indexed images, this can only be
  44.  *     done if there is at least one free colormap entry.
  45.  *
  46.  * Release 1.0, 1997-09-06, Gordon Matzigkeit <gord@gnu.ai.mit.edu>:
  47.  *   - Handle loading all image types from the 2.0 specification.
  48.  *   - Fix many alignment and endianness problems.
  49.  *   - Use tiles for lower memory consumption and better speed.
  50.  *   - Rewrite RLE code for clarity and speed.
  51.  *   - Handle saving with RLE.
  52.  *
  53.  * Release 0.9, 1997-06-18, Raphael FRANCOIS <fraph@ibm.net>:
  54.  *   - Can load 24 and 32-bit Truecolor images, with and without RLE.
  55.  *   - Saving currently only works without RLE.
  56.  *
  57.  *
  58.  * TODO:
  59.  *   - The GIMP stores the indexed alpha channel as a separate byte,
  60.  *     one for each pixel.  The TGA file format spec requires that the
  61.  *     alpha channel be stored as part of the colormap, not with each
  62.  *     individual pixel.  This means that we have no good way of
  63.  *     saving and loading INDEXEDA images that use alpha channel values
  64.  *     other than 0 and 255.  Find a workaround.
  65.  */
  66.  
  67. /* Set these for debugging. */
  68. /* #define PROFILE 1 */
  69.  
  70. #include "config.h"
  71.  
  72. #ifdef PROFILE
  73. # include <sys/times.h>
  74. #endif
  75.  
  76. #include <stdio.h>
  77. #include <stdlib.h>
  78. #include <string.h>
  79. #ifdef HAVE_UNISTD_H
  80. #include <unistd.h>
  81. #endif
  82.  
  83. #include <gtk/gtk.h>
  84.  
  85. #include <libgimp/gimp.h>
  86. #include <libgimp/gimpui.h>
  87.  
  88. #include "libgimp/stdplugins-intl.h"
  89.  
  90.  
  91. /* Round up a division to the nearest integer. */
  92. #define ROUNDUP_DIVIDE(n,d) (((n) + (d - 1)) / (d))
  93.  
  94. typedef struct _TgaSaveVals
  95. {
  96.   gint rle;
  97.   gint origin;
  98. } TgaSaveVals;
  99.  
  100. static TgaSaveVals tsvals =
  101. {
  102.   1,    /* rle = ON */
  103.   1,     /* origin = bottom left */
  104. };
  105.  
  106. typedef struct _TgaSaveInterface
  107. {
  108.   gint run;
  109. } TgaSaveInterface;
  110.  
  111. static TgaSaveInterface tsint =
  112. {
  113.   FALSE                /*  run  */
  114. };
  115.  
  116.  
  117.  /* TRUEVISION-XFILE magic signature string */
  118. static guchar magic[18] =
  119. {
  120.   0x54, 0x52, 0x55, 0x45, 0x56, 0x49, 0x53, 0x49, 0x4f,
  121.   0x4e, 0x2d, 0x58, 0x46, 0x49, 0x4c, 0x45, 0x2e, 0x0
  122. };
  123.  
  124. typedef struct tga_info_struct
  125. {
  126.   guint8 idLength;
  127.   guint8 colorMapType;
  128.  
  129.   guint8 imageType;
  130.   /* Known image types. */
  131. #define TGA_TYPE_MAPPED      1
  132. #define TGA_TYPE_COLOR       2
  133. #define TGA_TYPE_GRAY        3
  134.  
  135.   guint8 imageCompression;
  136.   /* Only known compression is RLE */
  137. #define TGA_COMP_NONE        0 
  138. #define TGA_COMP_RLE         1 
  139.  
  140.   /* Color Map Specification. */
  141.   /* We need to separately specify high and low bytes to avoid endianness
  142.      and alignment problems. */
  143.  
  144.   guint16 colorMapIndex;
  145.   guint16 colorMapLength;
  146.   guint8 colorMapSize;
  147.  
  148.   /* Image Specification. */
  149.   guint16 xOrigin;
  150.   guint16 yOrigin;
  151.  
  152.   guint16 width;
  153.   guint16 height;
  154.  
  155.   guint8 bpp;
  156.   guint8 bytes;
  157.  
  158.   guint8 alphaBits;
  159.   guint8 flipHoriz;
  160.   guint8 flipVert;
  161.  
  162.   /* Extensions (version 2) */
  163.  
  164. /* Not all the structures described in the standard are transcribed here
  165.    only those which seem applicable to Gimp */
  166.  
  167.   gchar authorName[41];
  168.   gchar comment[324];
  169.   guint month, day, year, hour, minute, second;
  170.   gchar jobName[41];
  171.   gchar softwareID[41];
  172.   guint pixelWidth, pixelHeight;  /* write dpi? */
  173.   gdouble gamma;
  174. } tga_info;
  175.  
  176.  
  177. /* Declare some local functions.
  178.  */
  179. static void   query               (void);
  180. static void   run                 (gchar      *name,
  181.                    gint        nparams,
  182.                    GimpParam  *param,
  183.                    gint       *nreturn_vals,
  184.                    GimpParam **return_vals);
  185.  
  186. static gint32 load_image           (gchar     *filename);
  187. static gint   save_image           (gchar     *filename,
  188.                     gint32     image_ID,
  189.                     gint32     drawable_ID);
  190.  
  191. static gint   save_dialog          (void);
  192. static void   save_ok_callback     (GtkWidget *widget,
  193.                     gpointer   data);
  194.  
  195. static gint32 ReadImage            (FILE      *fp,
  196.                     tga_info  *info,
  197.                     gchar     *filename);
  198.  
  199.  
  200. GimpPlugInInfo PLUG_IN_INFO =
  201. {
  202.   NULL,  /* init_proc  */
  203.   NULL,  /* quit_proc  */
  204.   query, /* query_proc */
  205.   run,   /* run_proc   */
  206. };
  207.  
  208.  
  209. MAIN ()
  210.  
  211. static void
  212. query (void)
  213. {
  214.   static GimpParamDef load_args[] =
  215.   {
  216.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  217.     { GIMP_PDB_STRING, "filename", "The name of the file to load" },
  218.     { GIMP_PDB_STRING, "raw_filename", "The name entered" }
  219.   };
  220.   static gint nload_args = sizeof (load_args) / sizeof (load_args[0]);
  221.  
  222.   static GimpParamDef load_return_vals[] =
  223.   {
  224.     { GIMP_PDB_IMAGE, "image", "Output image" }
  225.   };
  226.   static gint nload_return_vals = (sizeof (load_return_vals) /
  227.                    sizeof (load_return_vals[0]));
  228.  
  229.   static GimpParamDef save_args[] =
  230.   {
  231.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  232.     { GIMP_PDB_IMAGE, "image", "Input image" },
  233.     { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
  234.     { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
  235.     { GIMP_PDB_STRING, "raw_filename", "The name of the file to save the image in" },
  236.     { GIMP_PDB_INT32, "rle", "Use RLE compression" },
  237.     { GIMP_PDB_INT32, "origin", "Image origin (0 = top-left, 1 = bottom-left)"}
  238.  
  239.   } ;
  240.   static gint nsave_args = sizeof (save_args) / sizeof (save_args[0]);
  241.  
  242.   gimp_install_procedure ("file_tga_load",
  243.                           "Loads files of Targa file format",
  244.                           "FIXME: write help for tga_load",
  245.                           "Raphael FRANCOIS, Gordon Matzigkeit",
  246.                           "Raphael FRANCOIS, Gordon Matzigkeit",
  247.                           "1997",
  248.                           "<Load>/TGA",
  249.                           NULL,
  250.                           GIMP_PLUGIN,
  251.                           nload_args, nload_return_vals,
  252.                           load_args, load_return_vals);
  253.  
  254.   gimp_install_procedure ("file_tga_save",
  255.                           "saves files in the Targa file format",
  256.                           "FIXME: write help for tga_save",
  257.               "Raphael FRANCOIS, Gordon Matzigkeit",
  258.                           "Raphael FRANCOIS, Gordon Matzigkeit",
  259.                           "1997",
  260.                           "<Save>/TGA",
  261.               "RGB*, GRAY*, INDEXED*",
  262.                           GIMP_PLUGIN,
  263.                           nsave_args, 0,
  264.                           save_args, NULL);
  265.  
  266.   gimp_register_load_handler ("file_tga_load", "tga", "");
  267.         
  268.   gimp_register_save_handler ("file_tga_save", "tga", "");
  269. }
  270.  
  271. static void
  272. run (gchar      *name,
  273.      gint        nparams,
  274.      GimpParam  *param,
  275.      gint       *nreturn_vals,
  276.      GimpParam **return_vals)
  277. {
  278.   static GimpParam     values[2];
  279.   GimpRunModeType      run_mode;
  280.   GimpPDBStatusType    status = GIMP_PDB_SUCCESS;
  281.   gint32               image_ID;
  282.   gint32               drawable_ID;
  283.   GimpExportReturnType export = GIMP_EXPORT_CANCEL;
  284.  
  285. #ifdef PROFILE
  286.   struct tms tbuf1, tbuf2;
  287. #endif
  288.  
  289.   run_mode = param[0].data.d_int32;
  290.  
  291.   *nreturn_vals = 1;
  292.   *return_vals  = values;
  293.   values[0].type          = GIMP_PDB_STATUS;
  294.   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
  295.  
  296.   if (strcmp (name, "file_tga_load") == 0)
  297.     {
  298.       INIT_I18N();
  299.  
  300. #ifdef PROFILE
  301.       times (&tbuf1);
  302. #endif
  303.  
  304.       image_ID = load_image (param[1].data.d_string);
  305.  
  306.       if (image_ID != -1)
  307.         {
  308.           *nreturn_vals = 2;
  309.           values[1].type         = GIMP_PDB_IMAGE;
  310.           values[1].data.d_image = image_ID;
  311.         }
  312.       else
  313.         {
  314.           status = GIMP_PDB_EXECUTION_ERROR;
  315.         }
  316.     }
  317.   else if (strcmp (name, "file_tga_save") == 0)
  318.     {
  319.       INIT_I18N_UI();
  320.       gimp_ui_init ("tga", FALSE);
  321.  
  322.       image_ID     = param[1].data.d_int32;
  323.       drawable_ID  = param[2].data.d_int32;
  324.  
  325.       /*  eventually export the image */ 
  326.       switch (run_mode)
  327.     {
  328.     case GIMP_RUN_INTERACTIVE:
  329.     case GIMP_RUN_WITH_LAST_VALS:
  330.       export = gimp_export_image (&image_ID, &drawable_ID, "TGA", 
  331.                       (GIMP_EXPORT_CAN_HANDLE_RGB |
  332.                        GIMP_EXPORT_CAN_HANDLE_GRAY |
  333.                        GIMP_EXPORT_CAN_HANDLE_INDEXED | 
  334.                        GIMP_EXPORT_CAN_HANDLE_ALPHA ));
  335.       if (export == GIMP_EXPORT_CANCEL)
  336.         {
  337.           values[0].data.d_status = GIMP_PDB_CANCEL;
  338.           return;
  339.         }
  340.       break;
  341.     default:
  342.       break;
  343.     }
  344.  
  345.       switch (run_mode)
  346.     {
  347.     case GIMP_RUN_INTERACTIVE:
  348.       /*  Possibly retrieve data  */
  349.       gimp_get_data ("file_tga_save", &tsvals);
  350.  
  351.       /*  First acquire information with a dialog  */
  352.       if (! save_dialog ())
  353.         status = GIMP_PDB_CANCEL;
  354.       break;
  355.  
  356.     case GIMP_RUN_NONINTERACTIVE:
  357.       /*  Make sure all the arguments are there!  */
  358.       if (nparams != 7)
  359.         {
  360.           status = GIMP_PDB_CALLING_ERROR;
  361.         }
  362.       else
  363.         {
  364.           tsvals.rle = (param[5].data.d_int32) ? TRUE : FALSE;
  365.         }
  366.       break;
  367.  
  368.     case GIMP_RUN_WITH_LAST_VALS:
  369.       /*  Possibly retrieve data  */
  370.       gimp_get_data ("file_tga_save", &tsvals);
  371.       break;
  372.  
  373.     default:
  374.       break;
  375.     }
  376.  
  377. #ifdef PROFILE
  378.       times (&tbuf1);
  379. #endif
  380.  
  381.       if (status == GIMP_PDB_SUCCESS)
  382.     {
  383.       if (save_image (param[3].data.d_string, image_ID, drawable_ID))
  384.         {
  385.           /*  Store psvals data  */
  386.           gimp_set_data ("file_tga_save", &tsvals, sizeof (tsvals));
  387.         }
  388.       else
  389.         {
  390.           status = GIMP_PDB_EXECUTION_ERROR;
  391.         }
  392.     }
  393.  
  394.       if (export == GIMP_EXPORT_EXPORT)
  395.     gimp_image_delete (image_ID);
  396.     }
  397.   else
  398.     {
  399.       status = GIMP_PDB_CALLING_ERROR;
  400.     }
  401.  
  402.   values[0].data.d_status = status;
  403.  
  404. #ifdef PROFILE
  405.   times (&tbuf2);
  406.   printf ("TGA: %s profile: %ld user %ld system\n", name,
  407.       (long) tbuf2.tms_utime - tbuf1.tms_utime,
  408.       (long) tbuf2.tms_stime - tbuf2.tms_stime);
  409. #endif
  410. }
  411.  
  412. static gint32
  413. load_image (gchar *filename)
  414. {
  415.   FILE     *fp;
  416.   gchar    *name_buf;
  417.   tga_info  info;
  418.   guchar    header[18];
  419.   guchar    footer[26];
  420.   guchar    extension[495];
  421.   long      offset;
  422.  
  423.   gint32 image_ID = -1;
  424.  
  425.   fp = fopen (filename, "rb");
  426.   if (!fp)
  427.     {
  428.       g_message (_("TGA: can't open \"%s\"\n"), filename);
  429.       return -1;
  430.     }
  431.  
  432.   name_buf = g_strdup_printf( _("Loading %s:"), filename);
  433.   gimp_progress_init (name_buf);
  434.   g_free (name_buf);
  435.  
  436.   if (!fseek (fp, -26L, SEEK_END)) { /* Is file big enough for a footer? */
  437.     if (fread (footer, sizeof (footer), 1, fp) != 1) {
  438.       g_message (_("TGA: Cannot read footer from \"%s\"\n"), filename);
  439.       return -1;
  440.     } else if (memcmp (footer + 8, magic, sizeof (magic)) == 0) {
  441.  
  442.        /* Check the signature. */
  443.  
  444.       offset= footer[0] + (footer[1] * 256) + (footer[2] * 65536)
  445.                         + (footer[3] * 16777216);
  446.  
  447.       if (offset != 0) {
  448.         if (fseek (fp, offset, SEEK_SET) ||
  449.             fread (extension, sizeof (extension), 1, fp) != 1) {
  450.           g_message (_("TGA: Cannot read extension from \"%s\"\n"), filename);
  451.           return -1;
  452.         }
  453.         /* Eventually actually handle version 2 TGA here */
  454.       }
  455.  
  456.     }
  457.   }
  458.  
  459.   if (fseek (fp, 0, SEEK_SET) ||
  460.       fread (header, sizeof (header), 1, fp) != 1)
  461.     {
  462.       g_message ("TGA: Cannot read header from \"%s\"\n", filename);
  463.       return -1;
  464.     }
  465.  
  466.   switch (header[2])
  467.     {
  468.     case 1:
  469.       info.imageType = TGA_TYPE_MAPPED;
  470.       info.imageCompression = TGA_COMP_NONE;
  471.       break;
  472.     case 2:
  473.       info.imageType = TGA_TYPE_COLOR;
  474.       info.imageCompression = TGA_COMP_NONE;
  475.       break;
  476.     case 3:
  477.       info.imageType = TGA_TYPE_GRAY;
  478.       info.imageCompression = TGA_COMP_NONE;
  479.       break;
  480.  
  481.     case 9:
  482.       info.imageType = TGA_TYPE_MAPPED;
  483.       info.imageCompression = TGA_COMP_RLE;
  484.       break;
  485.     case 10:
  486.       info.imageType = TGA_TYPE_COLOR;
  487.       info.imageCompression = TGA_COMP_RLE;
  488.       break;
  489.     case 11:
  490.       info.imageType = TGA_TYPE_GRAY;
  491.       info.imageCompression = TGA_COMP_RLE;
  492.       break;
  493.  
  494.     default:
  495.       info.imageType = 0;
  496.     }
  497.  
  498.   info.idLength     = header[0];
  499.   info.colorMapType = header[1];
  500.  
  501.   info.colorMapIndex  = header[3] + header[4] * 256;
  502.   info.colorMapLength = header[5] + header[6] * 256;
  503.   info.colorMapSize   = header[7];
  504.  
  505.   info.xOrigin = header[8] + header[9] * 256;
  506.   info.yOrigin = header[10] + header[11] * 256;
  507.   info.width   = header[12] + header[13] * 256;
  508.   info.height  = header[14] + header[15] * 256;
  509.   
  510.   info.bpp       = header[16];
  511.   info.bytes     = (info.bpp + 7) / 8;
  512.   info.alphaBits = header[17] & 0x0f; /* Just the low 4 bits */
  513.   info.flipHoriz = (header[17] & 0x10) ? 1 : 0;
  514.   info.flipVert  = (header[17] & 0x20) ? 0 : 1;
  515.  
  516.   switch (info.imageType)
  517.     {
  518.       case TGA_TYPE_MAPPED:
  519.         if (info.bpp != 8)
  520.           {
  521.             g_message ("TGA: Unhandled sub-format in \"%s\"\n", filename);
  522.             return -1;
  523.           }
  524.         break;
  525.       case TGA_TYPE_COLOR:
  526.         if (info.bpp != 15 && info.bpp != 16 && info.bpp != 24
  527.                      && info.bpp != 32)
  528.           {
  529.             g_message ("TGA: Unhandled sub-format in \"%s\"\n", filename);
  530.             return -1;
  531.           }
  532.         break;
  533.       case TGA_TYPE_GRAY:
  534.         if (info.bpp != 8 && (info.alphaBits != 8 || (info.bpp != 16 || info.bpp != 15)))
  535.           {
  536.             g_message ("TGA: Unhandled sub-format in \"%s\"\n", filename);
  537.             return -1;
  538.           }
  539.         break;
  540.  
  541.       default:
  542.         g_message ("TGA: Unknown image type for \"%s\"\n", filename);
  543.         return -1;
  544.     }
  545.  
  546.   /* Plausible but unhandled formats */
  547.   if (info.bytes * 8 != info.bpp && !(info.bytes == 2 && info.bpp == 15))
  548.     {
  549.       g_message ("TGA: No support yet for TGA with these parameters\n");
  550.       return -1;
  551.     }
  552.  
  553.   /* Check that we have a color map only when we need it. */
  554.   if (info.imageType == TGA_TYPE_MAPPED && info.colorMapType != 1)
  555.     {
  556.       g_message ("TGA: indexed image has invalid color map type %d\n",
  557.                   info.colorMapType);
  558.       return -1;
  559.     }
  560.   else if (info.imageType != TGA_TYPE_MAPPED && info.colorMapType != 0)
  561.     {
  562.       g_message ("TGA: non-indexed image has invalid color map type %d\n",
  563.                   info.colorMapType);
  564.       return -1;
  565.     }
  566.  
  567.   /* Skip the image ID field. */
  568.   if (info.idLength && fseek (fp, info.idLength, SEEK_CUR))
  569.     {
  570.       g_message ("TGA: File is truncated or corrupted \"%s\"\n", filename);
  571.       return -1;
  572.     }
  573.  
  574.   image_ID = ReadImage (fp, &info, filename);
  575.   fclose (fp);
  576.   return image_ID;
  577. }
  578.  
  579. static void
  580. rle_write (FILE   *fp,
  581.        guchar *buffer,
  582.        guint   width,
  583.        guint   bytes)
  584. {
  585.   gint    repeat = 0;
  586.   gint    direct = 0;
  587.   guchar *from   = buffer;
  588.   gint    x;
  589.   
  590.   for (x = 1; x < width; ++x)
  591.     {
  592.       if (memcmp (buffer, buffer + bytes, bytes))
  593.     {
  594.       /* next pixel is different */
  595.       if (repeat)
  596.         {
  597.           putc (128 + repeat, fp);
  598.           fwrite (from, bytes, 1, fp);
  599.           from = buffer+ bytes; /* point to first different pixel */
  600.           repeat = 0;
  601.           direct = 0;
  602.         }
  603.       else
  604.         {
  605.           direct += 1;
  606.         }
  607.     }
  608.       else
  609.     {
  610.       /* next pixel is the same */
  611.       if (direct)
  612.         {
  613.           putc (direct - 1, fp);
  614.           fwrite (from, bytes, direct, fp);
  615.           from = buffer; /* point to first identical pixel */
  616.           direct = 0;
  617.           repeat = 1;
  618.         }
  619.       else
  620.         {
  621.           repeat += 1;
  622.         }
  623.     }
  624.  
  625.       if (repeat == 128)
  626.     {
  627.       putc (255, fp);
  628.       fwrite (from, bytes, 1, fp);
  629.       from = buffer+ bytes;
  630.       direct = 0;
  631.       repeat = 0;
  632.     }
  633.       else if (direct == 128)
  634.     {
  635.       putc (127, fp);
  636.       fwrite (from, bytes, direct, fp);
  637.       from = buffer+ bytes;
  638.       direct = 0;
  639.       repeat = 0;
  640.     }
  641.  
  642.       buffer += bytes;
  643.     }
  644.  
  645.   if (repeat > 0)
  646.     {
  647.       putc (128 + repeat, fp);
  648.       fwrite (from, bytes, 1, fp);
  649.     }
  650.   else
  651.     {
  652.       putc (direct, fp);
  653.       fwrite (from, bytes, direct + 1, fp);
  654.     }
  655. }
  656.  
  657. static gint
  658. rle_read (FILE     *fp,
  659.       guchar   *buffer,
  660.       tga_info *info)
  661. {
  662.   static gint   repeat = 0;
  663.   static gint   direct = 0;
  664.   static guchar sample[4];
  665.   gint head;
  666.   gint x, k;
  667.  
  668.   for (x = 0; x < info->width; x++)
  669.     {
  670.       if (repeat == 0 && direct == 0)
  671.     {
  672.       head = getc (fp);
  673.  
  674.       if (head == EOF)
  675.         {
  676.           return EOF;
  677.         }
  678.       else if (head >= 128)
  679.         {
  680.           repeat = head - 127;
  681.  
  682.           if (fread (sample, info->bytes, 1, fp) < 1)
  683.         return EOF;
  684.         }
  685.       else
  686.         {
  687.           direct = head + 1;
  688.         }
  689.     }
  690.  
  691.       if (repeat > 0)
  692.     {
  693.       for (k = 0; k < info->bytes; ++k)
  694.         {
  695.           buffer[k] = sample[k];
  696.         }
  697.  
  698.       repeat--;
  699.     }
  700.       else /* direct > 0 */
  701.     {
  702.       if (fread (buffer, info->bytes, 1, fp) < 1)
  703.         return EOF;
  704.  
  705.       direct--;
  706.     }
  707.  
  708.       buffer += info->bytes;
  709.     }
  710.  
  711.   return 0;
  712. }
  713.  
  714. static void
  715. flip_line (guchar   *buffer,
  716.        tga_info *info)
  717. {
  718.   guchar  temp;
  719.   guchar *alt;
  720.   gint    x, s;
  721.  
  722.   alt = buffer + (info->bytes * (info->width - 1));
  723.  
  724.   for (x = 0; x * 2 <= info->width; x++)
  725.     {
  726.       for (s = 0; s < info->bytes; ++s)
  727.     {
  728.       temp = buffer[s];
  729.       buffer[s] = alt[s];
  730.       alt[s] = temp;
  731.     }
  732.  
  733.       buffer += info->bytes;
  734.       alt -= info->bytes;
  735.     }
  736. }
  737.  
  738. /* Some people write 16-bit RGB TGA files. The spec would probably
  739.    allow 27-bit RGB too, for what it's worth, but I won't fix that
  740.    unless someone actually provides an existence proof */
  741.  
  742. static void
  743. upsample (guchar *dest,
  744.       guchar *src,
  745.       guint   width,
  746.       guint   bytes)
  747. {
  748.   gint x;
  749.  
  750.   for (x = 0; x < width; x++)
  751.     {
  752.       dest[0] =  ((src[1] << 1) & 0xf8);
  753.       dest[0] += (dest[0] >> 5);
  754.  
  755.       dest[1] =  ((src[0] & 0xe0) >> 2) + ((src[1] & 0x03) << 6);
  756.       dest[1] += (dest[1] >> 5);
  757.  
  758.       dest[2] =  ((src[0] << 3) & 0xf8);
  759.       dest[2] += (dest[2] >> 5);
  760.  
  761.       dest += 3;
  762.       src += bytes;
  763.     }
  764. }
  765.  
  766. static void
  767. bgr2rgb (guchar *dest,
  768.      guchar *src,
  769.      guint   width,
  770.      guint   bytes,
  771.      guint   alpha)
  772. {
  773.   guint x;
  774.  
  775.   if (alpha)
  776.     {
  777.       for (x = 0; x < width; x++)
  778.     {
  779.       *(dest++) = src[2];
  780.       *(dest++) = src[1];
  781.       *(dest++) = src[0];
  782.       *(dest++) = src[3];
  783.  
  784.       src += bytes;
  785.     }
  786.     }
  787.   else
  788.     {
  789.       for (x = 0; x < width; x++)
  790.     {
  791.       *(dest++) = src[2];
  792.       *(dest++) = src[1];
  793.       *(dest++) = src[0];
  794.  
  795.       src += bytes;
  796.     }
  797.     }
  798. }
  799.  
  800. static void
  801. read_line (FILE         *fp,
  802.        guchar       *row,
  803.        guchar       *buffer,
  804.        tga_info     *info,
  805.        GimpDrawable *drawable)
  806. {
  807.   if (info->imageCompression == TGA_COMP_RLE)
  808.     {
  809.       rle_read (fp, buffer, info);
  810.     }
  811.   else
  812.     {
  813.       fread (buffer, info->bytes, info->width, fp);
  814.     }
  815.  
  816.   if (info->flipHoriz)
  817.     {
  818.       flip_line (buffer, info);
  819.     }
  820.  
  821.   if (info->imageType == TGA_TYPE_COLOR)
  822.     {
  823.       if (info->bpp == 16 || info->bpp == 15)
  824.     {
  825.       upsample (row, buffer, info->width, info->bytes);
  826.     }
  827.       else
  828.     {
  829.       bgr2rgb (row, buffer,info->width,info->bytes,info->alphaBits);
  830.     }
  831.     }
  832.   else
  833.     {
  834.       memcpy (row, buffer, info->width * drawable->bpp);
  835.     }
  836. }
  837.  
  838. static gint32
  839. ReadImage (FILE     *fp,
  840.        tga_info *info,
  841.        gchar    *filename)
  842. {
  843.   static gint32 image_ID;
  844.   gint32        layer_ID;
  845.  
  846.   GimpPixelRgn       pixel_rgn;
  847.   GimpDrawable      *drawable;
  848.   guchar            *data, *buffer, *row;
  849.   GimpImageType      dtype = 0;
  850.   GimpImageBaseType  itype = 0;
  851.   gint               i, y;
  852.  
  853.   gint max_tileheight, tileheight;
  854.  
  855.   guint  cmap_bytes;
  856.   guchar tga_cmap[4 * 256], gimp_cmap[3 * 256];
  857.  
  858.   switch (info->imageType)
  859.     {
  860.     case TGA_TYPE_MAPPED:
  861.       itype = GIMP_INDEXED;
  862.  
  863.       if (info->alphaBits)
  864.     dtype = GIMP_INDEXEDA_IMAGE;
  865.       else
  866.     dtype = GIMP_INDEXED_IMAGE;
  867.       break;
  868.  
  869.     case TGA_TYPE_GRAY:
  870.       itype = GIMP_GRAY;
  871.  
  872.       if (info->alphaBits)
  873.     dtype = GIMP_GRAYA_IMAGE;
  874.       else
  875.     dtype = GIMP_GRAY_IMAGE;
  876.       break;
  877.  
  878.     case TGA_TYPE_COLOR:
  879.       itype = GIMP_RGB;
  880.  
  881.       if (info->alphaBits)
  882.     dtype = GIMP_RGBA_IMAGE;
  883.       else
  884.     dtype = GIMP_RGB_IMAGE;
  885.       break;
  886.     }
  887.  
  888.   /* Handle colormap */
  889.  
  890.   if (info->colorMapType == 1)
  891.     {
  892.       cmap_bytes= (info->colorMapSize + 7 ) / 8;
  893.       if (cmap_bytes <= 4 &&
  894.           fread (tga_cmap, info->colorMapLength * cmap_bytes, 1, fp) == 1)
  895.         {
  896.           if (info->colorMapSize == 32)
  897.             bgr2rgb(gimp_cmap, tga_cmap, info->colorMapLength, cmap_bytes, 1);
  898.           else if (info->colorMapSize == 24)
  899.             bgr2rgb(gimp_cmap, tga_cmap, info->colorMapLength, cmap_bytes, 0);
  900.           else if (info->colorMapSize == 16 || info->colorMapSize == 15)
  901.             upsample(gimp_cmap, tga_cmap, info->colorMapLength, cmap_bytes);
  902.  
  903.         }
  904.       else
  905.         {
  906.           g_message ("TGA: File is truncated or corrupted \"%s\"\n", filename);
  907.           return -1;
  908.         }
  909.     }
  910.  
  911.   image_ID = gimp_image_new (info->width, info->height, itype);
  912.   gimp_image_set_filename (image_ID, filename);
  913.  
  914.   if (info->colorMapType == 1)
  915.     gimp_image_set_cmap(image_ID, gimp_cmap, info->colorMapLength);
  916.  
  917.   layer_ID = gimp_layer_new (image_ID,
  918.                  _("Background"),
  919.                  info->width, info->height,
  920.                  dtype, 100,
  921.                  GIMP_NORMAL_MODE);
  922.  
  923.   gimp_image_add_layer (image_ID, layer_ID, 0);
  924.  
  925.   drawable = gimp_drawable_get (layer_ID);
  926.  
  927.   /* Prepare the pixel region. */
  928.   gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0,
  929.                         info->width, info->height, TRUE, FALSE);
  930.  
  931.   /* Allocate the data. */
  932.   max_tileheight = gimp_tile_height ();
  933.   data = (guchar *) g_malloc (info->width * max_tileheight * drawable->bpp);
  934.   buffer = (guchar *) g_malloc (info->width * info->bytes);
  935.  
  936.   if (info->flipVert)
  937.     {
  938.       for (i = 0; i < info->height; i += tileheight)
  939.         {
  940.           tileheight = i ? max_tileheight : (info->height % max_tileheight);
  941.           if (tileheight == 0) tileheight= max_tileheight;
  942.  
  943.           for (y= 1; y <= tileheight; ++y)
  944.             {
  945.               row= data + (info->width * drawable->bpp * (tileheight - y));
  946.               read_line(fp, row, buffer, info, drawable);
  947.             }
  948.  
  949.             gimp_progress_update((double) (i + tileheight)
  950.                                / (double) info->height);
  951.             gimp_pixel_rgn_set_rect(&pixel_rgn, data, 0, 
  952.                                info->height - i - tileheight,  
  953.                                info->width, tileheight);
  954.         }
  955.     }
  956.   else
  957.     {
  958.       for (i = 0; i < info->height; i += max_tileheight)
  959.         {
  960.           tileheight = MIN (max_tileheight, info->height - i);
  961.  
  962.           for (y= 0; y < tileheight; ++y)
  963.             {
  964.               row= data + (info->width * drawable->bpp * y);
  965.               read_line(fp, row, buffer, info, drawable);
  966.             }
  967.  
  968.             gimp_progress_update((double) (i + tileheight)
  969.                                / (double) info->height);
  970.             gimp_pixel_rgn_set_rect(&pixel_rgn, data, 0, i, 
  971.                                info->width, tileheight);
  972.         }
  973.     }
  974.  
  975.   g_free (data);
  976.   g_free (buffer);
  977.  
  978.   gimp_drawable_flush (drawable);
  979.   gimp_drawable_detach (drawable);
  980.  
  981.   return image_ID;
  982. }  /*read_image*/
  983.  
  984.  
  985. static gint
  986. save_image (gchar  *filename,
  987.         gint32  image_ID,
  988.         gint32  drawable_ID)
  989. {
  990.   GimpPixelRgn   pixel_rgn;
  991.   GimpDrawable  *drawable;
  992.   GimpImageType  dtype;
  993.   gint           width;
  994.   gint           height;
  995.  
  996.   FILE     *fp;
  997.   guchar   *name_buf;
  998.   gint      tileheight;
  999.   gint      out_bpp = 0;
  1000.   gboolean  status  = TRUE;
  1001.   gint      i, row;
  1002.  
  1003.   guchar  header[18];
  1004.   guchar  footer[26];
  1005.   guchar *pixels;
  1006.   guchar *data;
  1007.  
  1008.   guint   num_colors;
  1009.   guchar *gimp_cmap = NULL;
  1010.  
  1011.   drawable = gimp_drawable_get (drawable_ID);
  1012.   dtype    = gimp_drawable_type (drawable_ID);
  1013.  
  1014.   width  = drawable->width;
  1015.   height = drawable->height;
  1016.  
  1017.   name_buf = g_strdup_printf (_("Saving %s:"), filename);
  1018.   gimp_progress_init ((gchar *)name_buf);
  1019.   g_free (name_buf);
  1020.  
  1021.   if ((fp = fopen (filename, "wb")) == NULL)
  1022.     {
  1023.       g_message ("TGA: can't create \"%s\"\n", filename);
  1024.       return FALSE;
  1025.     }
  1026.  
  1027.   header[0] = 0; /* No image identifier / description */
  1028.  
  1029.   if (dtype == GIMP_INDEXED_IMAGE || dtype == GIMP_INDEXEDA_IMAGE)
  1030.     {
  1031.       gimp_cmap = gimp_image_get_cmap (image_ID, &num_colors);
  1032.  
  1033.       header[1] = 1; /* cmap type */
  1034.       header[2] = (tsvals.rle) ? 9 : 1;
  1035.       header[3] = header[4]= 0; /* no offset */
  1036.       header[5] = num_colors % 256;
  1037.       header[6] = num_colors / 256; 
  1038.       header[7] = 24; /* cmap size / bits */
  1039.     }
  1040.   else
  1041.     {
  1042.       header[1]= 0;
  1043.  
  1044.       if (dtype == GIMP_RGB_IMAGE || dtype == GIMP_RGBA_IMAGE)
  1045.     {
  1046.       header[2]= (tsvals.rle) ? 10 : 2;
  1047.     }
  1048.       else
  1049.     {
  1050.       header[2]= (tsvals.rle) ? 11 : 3;
  1051.     }
  1052.  
  1053.       header[3] = header[4] = header[5] = header[6] = header[7] = 0;
  1054.     }
  1055.  
  1056.   header[8]  = header[9]  = 0; /* xorigin */
  1057.   header[10] = header[11] = 0; /* yorigin */
  1058.  
  1059.   header[12] = width % 256;
  1060.   header[13] = width / 256;
  1061.  
  1062.   header[14] = height % 256;
  1063.   header[15] = height / 256;
  1064.  
  1065.   switch (dtype)
  1066.     {
  1067.     case GIMP_INDEXED_IMAGE:
  1068.     case GIMP_GRAY_IMAGE:
  1069.     case GIMP_INDEXEDA_IMAGE:
  1070.       out_bpp = 1;
  1071.       header[16] = 8; /* bpp */
  1072.       header[17] = (tsvals.origin) ? 0 :  0x20; /* alpha + orientation */
  1073.       break;
  1074.  
  1075.     case GIMP_GRAYA_IMAGE:
  1076.       out_bpp = 2;
  1077.       header[16] = 16; /* bpp */
  1078.       header[17] = (tsvals.origin) ? 8 : 0x28; /* alpha + orientation */
  1079.       break;
  1080.  
  1081.     case GIMP_RGB_IMAGE:
  1082.       out_bpp = 3;
  1083.       header[16] = 24; /* bpp */
  1084.       header[17] = (tsvals.origin) ? 0 : 0x20; /* alpha + orientation */
  1085.       break;
  1086.  
  1087.     case GIMP_RGBA_IMAGE:
  1088.       out_bpp = 4;
  1089.       header[16] = 32; /* bpp */
  1090.       header[17] = (tsvals.origin) ? 8 : 0x28; /* alpha + orientation */
  1091.       break;
  1092.     }
  1093.  
  1094.   /* write header to front of file */
  1095.   fwrite (header, sizeof (header), 1, fp);
  1096.  
  1097.   if (dtype == GIMP_INDEXED_IMAGE || dtype == GIMP_INDEXEDA_IMAGE)
  1098.     {
  1099.       /* write out palette */
  1100.       for (i= 0; i < num_colors; ++i)
  1101.     {
  1102.       fputc (gimp_cmap[(i * 3) + 2], fp);
  1103.       fputc (gimp_cmap[(i * 3) + 1], fp);
  1104.       fputc (gimp_cmap[(i * 3) + 0], fp);
  1105.     }
  1106.     }
  1107.  
  1108.   /* Allocate a new set of pixels. */
  1109.   tileheight = gimp_tile_height ();
  1110.  
  1111.   gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, width, height, FALSE, FALSE);
  1112.  
  1113.   pixels = g_new (guchar, width * drawable->bpp);
  1114.   data   = g_new (guchar, width * out_bpp);
  1115.  
  1116.   for (row = 0; row < height; ++row)
  1117.     {
  1118.       if(tsvals.origin)
  1119.         {
  1120.           gimp_pixel_rgn_get_rect (&pixel_rgn, pixels, 0, height-(row+1), width, 1);
  1121.         } 
  1122.       else 
  1123.         {
  1124.           gimp_pixel_rgn_get_rect (&pixel_rgn, pixels, 0, row, width, 1);
  1125.         }
  1126.  
  1127.       if (dtype == GIMP_RGB_IMAGE)
  1128.     {
  1129.       bgr2rgb(data, pixels, width, drawable->bpp, 0);
  1130.     }
  1131.       else if (dtype == GIMP_RGBA_IMAGE)
  1132.     {
  1133.       bgr2rgb(data, pixels, width, drawable->bpp, 1);
  1134.     }
  1135.       else if (dtype == GIMP_INDEXEDA_IMAGE)
  1136.     {
  1137.       for (i = 0; i < width; ++i)
  1138.         {
  1139.           data[i]= pixels[i*2];
  1140.         }
  1141.     }
  1142.       else
  1143.     {
  1144.       memcpy (data, pixels, width * drawable->bpp);
  1145.     }
  1146.  
  1147.       if (tsvals.rle)
  1148.     {
  1149.       rle_write (fp, data, width, out_bpp);
  1150.     }
  1151.       else
  1152.     {
  1153.       fwrite (data, width * out_bpp, 1, fp);
  1154.     }
  1155.  
  1156.       gimp_progress_update ((gdouble) row / (gdouble) height);
  1157.     }
  1158.  
  1159.   gimp_drawable_detach (drawable);
  1160.   g_free (data);
  1161.  
  1162.   /* footer must be the last thing written to file */
  1163.   memset (footer, 0, 8); /* No extensions, no developer directory */
  1164.   memcpy (footer + 8, magic, sizeof (magic)); /* magic signature */
  1165.   fwrite (footer, sizeof (footer), 1, fp);
  1166.  
  1167.   fclose (fp);
  1168.  
  1169.   return status;
  1170. }
  1171.  
  1172. static gint
  1173. save_dialog (void)
  1174. {
  1175.   GtkWidget *dlg;
  1176.   GtkWidget *toggle;
  1177.   GtkWidget *origin;
  1178.   GtkWidget *frame;
  1179.   GtkWidget *vbox;
  1180.  
  1181.   dlg = gimp_dialog_new (_("Save as TGA"), "tga",
  1182.              gimp_standard_help_func, "filters/tga.html",
  1183.              GTK_WIN_POS_MOUSE,
  1184.              FALSE, TRUE, FALSE,
  1185.  
  1186.              _("OK"), save_ok_callback,
  1187.              NULL, NULL, NULL, TRUE, FALSE,
  1188.              _("Cancel"), gtk_widget_destroy,
  1189.              NULL, 1, NULL, FALSE, TRUE,
  1190.  
  1191.              NULL);
  1192.  
  1193.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  1194.               GTK_SIGNAL_FUNC (gtk_main_quit),
  1195.               NULL);
  1196.  
  1197.   /* regular tga parameter settings */
  1198.   frame = gtk_frame_new (_("Targa Options"));
  1199.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  1200.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  1201.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  1202.  
  1203.   vbox = gtk_vbox_new (FALSE, 2);
  1204.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  1205.   gtk_container_add (GTK_CONTAINER (frame), vbox);
  1206.  
  1207.   /*  rle  */
  1208.   toggle = gtk_check_button_new_with_label (_("RLE compression"));
  1209.   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  1210.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  1211.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  1212.               &tsvals.rle);
  1213.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), tsvals.rle);
  1214.   gtk_widget_show (toggle);
  1215.  
  1216.   /*  origin  */
  1217.   origin = gtk_check_button_new_with_label (_("Origin at bottom left"));
  1218.   gtk_box_pack_start (GTK_BOX (vbox), origin, FALSE, FALSE, 0);
  1219.   gtk_signal_connect (GTK_OBJECT (origin), "toggled",
  1220.               GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  1221.               &tsvals.origin);
  1222.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (origin), tsvals.origin);
  1223.   gtk_widget_show (origin);
  1224.  
  1225.   gtk_widget_show (vbox);
  1226.   gtk_widget_show (frame);
  1227.  
  1228.   gtk_widget_show (dlg);
  1229.  
  1230.   gtk_main ();
  1231.   gdk_flush ();
  1232.  
  1233.   return tsint.run;
  1234. }
  1235.  
  1236. static void
  1237. save_ok_callback (GtkWidget *widget,
  1238.           gpointer   data)
  1239. {
  1240.   tsint.run = TRUE;
  1241.  
  1242.   gtk_widget_destroy (GTK_WIDGET (data));
  1243. }
  1244.